package com.jzhao.gper.spring.v2.webmvc.servlet;

import com.jzhao.gper.spring.v2.annotation.JZRequestMapping;
import com.jzhao.gper.spring.v2.context.JZApplicationContext;
import com.jzhao.gper.spring.v2.annotation.JZController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JZDispatherServlet extends HttpServlet {
    private JZApplicationContext applicationContext;

    private List<JZHandlerMapping> handlerMappings = new ArrayList<JZHandlerMapping>();

    private Map<JZHandlerMapping,JZHandlerAdapter> handlerAdapters = new HashMap<JZHandlerMapping,JZHandlerAdapter>();

    private List<JZViewResolver> viewResolvers = new ArrayList<JZViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatch(req,resp);
        } catch (Exception e) {
            try {
                processDispatchResult(req,resp,new JZModelAndView("500"));
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, JZModelAndView jzModelAndView) throws Exception {
        if(null == jzModelAndView) { return; }
        if(this.viewResolvers.isEmpty()){ return; }
        for (JZViewResolver viewResolver : this.viewResolvers) {
            JZView  view = viewResolver.resolveViewName(jzModelAndView.getViewName());
            if(view!=null){
                view.render(jzModelAndView.getMdoel(),req,resp);
                return;
            }
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        JZHandlerMapping handler = getHandler(req);

        if(handler == null){
            processDispatchResult(req,resp,new JZModelAndView("404"));
            return;
        }

        JZHandlerAdapter ha = getHandlerAdapter(handler);

        JZModelAndView mv = ha.handler(req,resp,handler);

        processDispatchResult(req,resp,mv);
    }

    private JZHandlerAdapter getHandlerAdapter(JZHandlerMapping handler) {
        if(this.handlerAdapters.isEmpty()){
            return null;
        }
        return this.handlerAdapters.get(handler);
    }

    private JZHandlerMapping getHandler(HttpServletRequest req) {
        if(this.handlerMappings.isEmpty()){ return null; }
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url  = url.replaceAll(contextPath,"").replaceAll("/+","/");
        for (JZHandlerMapping handlerMapping : handlerMappings) {
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            if(!matcher.matches()) { continue; }
            return handlerMapping;
        }
        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        applicationContext = new JZApplicationContext(config.getInitParameter("contextConfigLocation"));
        
        initStrategies(applicationContext);
    }

    private void initStrategies(JZApplicationContext context) {
        
        initHandlerMappings(context);
        
        initHandlerAdapters(context);
        
        initViewResolvers(context);
    }

    private void initViewResolvers(JZApplicationContext context) {
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);
        for (File file : templateRootDir.listFiles()) {
            this.viewResolvers.add(new JZViewResolver(file));
        }
    }

    private void initHandlerAdapters(JZApplicationContext context) {
        for (JZHandlerMapping handlerMapping : handlerMappings) {
            this.handlerAdapters.put(handlerMapping,new JZHandlerAdapter());
        }
    }

    private void initHandlerMappings(JZApplicationContext context) {
        if(this.applicationContext.getBeanDefinitionCount()==0){
            return;
        }

        for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
            Object instance = applicationContext.getBean(beanName);

            Class<?> clazz = instance.getClass();

            if(!clazz.isAnnotationPresent(JZController.class)){ continue; }

            String baseUrl ="";
            if(clazz.isAnnotationPresent(JZRequestMapping.class)){
                baseUrl = clazz.getAnnotation(JZRequestMapping.class).value();
            }

            //只取public方法
            for (Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(JZRequestMapping.class)){ continue; }

                JZRequestMapping requestMapping = method.getAnnotation(JZRequestMapping.class);

                String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+","/");

                Pattern pattern = Pattern.compile(regex);

                handlerMappings.add(new JZHandlerMapping(pattern,instance,method));
                System.out.println("Mapped : " + regex + "," + method);
            }

        }

    }
}
